#!/usr/bin/env -S node --no-warnings --loader ts-node/esm
import 'dotenv/config.js';
import axios from 'axios';
import { Contact, Message, ScanStatus, WechatyBuilder, log, Room, Wechaty } from 'wechaty';
import express, { Request, Response, RequestHandler } from 'express';
import cors from 'cors';
import mongoose from 'mongoose';
import { AccessToken, ScheduledTask, ForwardingConfig, Bot } from './models.ts';

// MongoDB 配置
const mongoUrl = 'mongodb://admin:ssjhuinf777@47.109.198.160:27018/'; // MongoDB地址
const dbName = 'automation'; // 数据库名称

// 连接 MongoDB
mongoose.connect(mongoUrl, {
  dbName, // 使用指定的数据库
}).then(() => {
  log.info('StarterBot', 'MongoDB 连接成功');
  return true; // 返回一个值
}).catch((error) => {
  log.error('StarterBot', 'MongoDB 连接失败:', error);
  throw error; // 抛出异常
});

// 创建 Express 应用
const app = express();
const port = 3008;

app.use(cors({
  allowedHeaders: [ 'Content-Type', 'Authorization' ],
  credentials: false, // 注意：如果使用 '*'，不能设置 credentials 为 true
  methods: [ 'GET', 'POST', 'PUT', 'DELETE', 'OPTIONS' ],
  origin: '*', // 允许所有域名跨域
}));

// 消息状态存储，用于支持消息的多步操作
const messageState = new Map<string, { step: number; imageFiles: any[]; textMessages: string[] }>();

// 动态管理的 Wechaty 实例数组
const bots: Wechaty[] = [];

// 存储多个配置
let forwardingConfigs: { roomTopic: string; userName: string; forwardRoom: string }[] = [];

// 存储多个二维码链接
const currentQRCodeUrls: string[] = [];

// 动态存储 AccessToken
let accessToken: string | null = null;

// 定时任务存储
interface ScheduledTask {
  id: string;
  time: Date;
  content: string;
  target: string;
}
const scheduledTasks: ScheduledTask[] = [];
// const taskIdCounter = 0;

// 设置监听和转发的 API 接口
app.use(express.json()); // 解析 JSON 请求体

// 接口：设置 AccessToken
app.post('/setAccessToken', async (req: Request, res: Response) => {
  const { token } = req.body;
  if (typeof token === 'string' && token.trim()) {
    accessToken = token.trim();

    // 获取当前数据库连接信息
    const dbName = mongoose.connection.name;  // 获取数据库名称
    const collectionName = AccessToken.collection.name;  // 获取集合名称

    // 打印当前数据库和集合信息
    // eslint-disable-next-line no-console
    console.log(`当前数据库：${dbName}`);
    // eslint-disable-next-line no-console
    console.log(`当前集合：${collectionName}`);

    // 保存 AccessToken 到数据库
    try {
      await AccessToken.create({
        status: 1, // 设置为有效
        token: accessToken,
      });
      // eslint-disable-next-line no-console
      console.log(`AccessToken 已更新：${accessToken}`);
      res.status(200).json({ message: 'AccessToken 设置成功' });
    } catch (error) {
      console.error('保存 AccessToken 到数据库失败:', error);
      res.status(500).json({ message: '服务器错误，无法保存 AccessToken' });
    }
  } else {
    res.status(400).json({ message: '无效的 AccessToken 参数' });
  }
});

// 接口：设置机器人实例数量
const setBotCountHandler: RequestHandler = async (req: Request, res: Response) => {
  try {
    const { count } = req.body;
    if (typeof count !== 'number' || count < 1) {
      res.status(400).json({ message: '请提供有效的机器人实例数量 (大于等于 1)' });
      return;
    }

    // 调整机器人实例数量
    if (count > bots.length) {
      // 启动新的机器人实例
      for (let i = bots.length; i < count; i++) {
        const bot = WechatyBuilder.build({
          puppet: 'wechaty-puppet-wechat',
          puppetOptions: { uos: true },
        });
        const botName = `bot${i + 1}`;
        configureBot(bot, botName);
        bots.push(bot);

        try {
          // 启动机器人
          await bot.start();
          log.info('StarterBot', `Bot${i + 1} started.`);

          // 在数据库中记录新的机器人实例
          const newBot = new Bot({
            botName,
            status: 1,  // 启动状态
          });
          await newBot.save();
          log.info('StarterBot', `Bot${i + 1} saved to database: ${JSON.stringify(newBot)}`);
        } catch (e) {
          log.error('StarterBot', `Failed to start Bot${i + 1}:`, e);
        }
      }
    } else if (count < bots.length) {
      // 停止多余的机器人实例
      for (let i = bots.length - 1; i >= count; i--) {
        const bot = bots.pop();
        if (bot) {
          try {
            await bot.stop();
            log.info('StarterBot', `Bot${i + 1} stopped.`);

            // 更新数据库中对应机器人的状态
            await Bot.findOneAndUpdate(
              { botName: `bot${i + 1}` },
              { status: 0, updatedAt: Date.now() },  // 更新状态为已停止
            );
            log.info('StarterBot', `Bot${i + 1} status updated to stopped.`);
          } catch (e) {
            log.error('StarterBot', `Failed to stop Bot${i + 1}:`, e);
          }
        }
      }
    }

    // 返回多个二维码链接和机器人实例数量
    res.status(200).json({
      message: `机器人实例数量已调整为 ${count}`,
      qrcode: currentQRCodeUrls.length ? currentQRCodeUrls : [ '二维码链接尚未生成' ],
    });
  } catch (error) {
    log.error('StarterBot', 'Error in /setBotCount:', error);
    res.status(500).json({ message: '服务器内部错误' });
  }
};

// 接口：更新监听和转发的群聊设置
app.post('/configure', async (req: Request, res: Response) => {
  const configs = req.body;

  if (Array.isArray(configs) && configs.length > 0) {
    forwardingConfigs = configs;

    // 保存转发配置到数据库
    try {
      await ForwardingConfig.insertMany(configs.map(config => ({
        forwardRoom: config.forwardRoom,
        roomTopic: config.roomTopic,
        status: 0, // 配置状态为已配置
        userName: config.userName,
      })));
      log.info('StarterBot', `转发配置已保存：${JSON.stringify(configs)}`);
      res.status(200).send({
        message: `配置成功！已配置 ${configs.length} 个监听和转发规则`,
      });
      // eslint-disable-next-line no-console
      console.log(`已更新配置：${configs.length} 个监听和转发规则`);
    } catch (error) {
      console.error('保存转发配置到数据库失败:', error);
      res.status(500).send({
        message: '服务器错误，无法保存转发配置',
      });
    }
  } else {
    res.status(400).send({
      message: '参数缺失！请提供有效的配置数组',
    });
  }
});

// 注册设置机器人实例数量的接口
app.post('/setBotCount', setBotCountHandler);

app.post('/scheduleMessage', async (req: Request, res: Response) => {
  const { id, timeFormat, content, rooms } = req.body;

  if (!id || !timeFormat || !content || !rooms || !Array.isArray(rooms) || rooms.length === 0) {
    res.status(400).json({ message: '请提供 id、timeFormat、content 和 rooms 数组' });
    return;
  }

  const durationMatch = timeFormat.match(/^(\d+)([smhd])$/);
  if (!durationMatch) {
    res.status(400).json({ message: '无效的 timeFormat 参数，请使用格式如 "30m" (分钟)、"1h" (小时)' });
    return;
  }

  const [ _, amount, unit ] = durationMatch; // 提取数值和单位
  const multiplier = { d: 86400000, h: 3600000, m: 60000, s: 1000 }[unit as 's' | 'm' | 'h' | 'd']; // 单位转换
  const time = new Date(Date.now() + parseInt(amount) * multiplier);

  // 将任务保存到 MongoDB
  try {
    await Promise.all(rooms.map(async (roomName) => {
      const taskId = `${id}-${roomName}`;
      // 使用 await 等待异步操作完成
      await ScheduledTask.create({
        content,
        id: taskId,
        status: 0, // 任务状态为未完成
        target: roomName,
        time,
      });
    }));
    // eslint-disable-next-line no-console
    console.log(`已添加定时任务：${id}, 目标群聊：${rooms.join(', ')}, 时间：${time}`);
    scheduleNextTask();
    res.status(200).json({
      message: '定时任务已添加',
      tasks: rooms.map((roomName) => ({ id: `${id}-${roomName}`, roomName, time })),
    });
  } catch (error) {
    console.error('保存定时任务到数据库失败:', error);
    res.status(500).json({ message: '服务器错误，无法保存定时任务' });
  }
});

// 扫码事件处理函数
function onScan (qrcode: string, status: ScanStatus, botName: string) {
  if (status === ScanStatus.Waiting || status === ScanStatus.Timeout) {
    // 为每个机器人生成二维码链接
    const qrCodeUrl = `https://wechaty.js.org/qrcode/${encodeURIComponent(qrcode)}`;
    currentQRCodeUrls.push(qrCodeUrl);
    log.info(botName, `onScan: ${ScanStatus[status]}(${status}) - ${qrCodeUrl}`);
  } else {
    log.info(botName, `onScan: ${ScanStatus[status]}(${status})`);
  }
}

// 登录事件处理函数
function onLogin (user: Contact, botName: string) {
  log.info(botName, `${user} login`);
}

// 登出事件处理函数
function onLogout (user: Contact, botName: string) {
  log.info(botName, `${user} logout`);
}

// 消息事件处理函数
async function onMessage (msg: Message, bot: Wechaty) {
  try {
    const contact = msg.talker();
    const content = msg.text();
    const room: Room | undefined = msg.room();

    if (room) {
      const roomName: string = await room.topic();

      // 根据配置进行多个群聊的转发
      for (const config of forwardingConfigs) {
        if (roomName === config.roomTopic && contact.name() === config.userName) {
          log.info('StarterBot', `监听到 ${config.userName} 在群聊 ${config.roomTopic} 中的消息: ${content}`);

          // 获取目标转发群聊
          const forwardRoom = await bot.Room.find({ topic: config.forwardRoom });
          if (forwardRoom) {
            if (msg.type() === bot.Message.Type.Text) {
              await forwardRoom.say(content);
              log.info('StarterBot', `文字消息已转发到群聊: ${config.forwardRoom}`);
            }

            if (msg.type() === bot.Message.Type.Image) {
              const image = await msg.toFileBox();
              await forwardRoom.say(image);
              log.info('StarterBot', `图片消息已转发到群聊: ${config.forwardRoom}`);
            }
          } else {
            log.error('StarterBot', `未找到群聊 ${config.forwardRoom}`);
          }
        }
      }
    } else {
      log.info('StarterBot', `非群聊消息: 内容: ${content}, 联系人: ${contact.name()}`);
    }

    // 自动回复 "dong"（如果消息是 "ding"）
    if (content.toLowerCase() === 'ding') {
      await msg.say('dong');
      return;
    }

    // 处理群发文字消息
    if (content.startsWith('帮我群发')) {
      messageState.set(contact.id, { imageFiles: [], step: 1, textMessages: [] });
      await msg.say('好的，小主，请发送需要群发的文字或图片，发送完后请回复“完毕”。');
      return;
    }

    if (messageState.has(contact.id)) {
      const state = messageState.get(contact.id)!;

      if (state.step === 1) {
        if (content === '完毕') {
          await msg.say('小主，请提供要群发的群聊名称，多个群聊用@分隔，或输入“全部”群发到所有群聊');
          state.step = 2;
        } else {
          if (msg.type() === bot.Message.Type.Text) {
            state.textMessages.push(content.trim());
          }
          if (msg.type() === bot.Message.Type.Image) {
            const image = await msg.toFileBox();
            state.imageFiles.push(image);
          }
        }
      } else if (state.step === 2) {
        const roomNames = content.trim().toLowerCase() === '全部'
          ? (await bot.Room.findAll()).map((room: Room) => room.topic())
          : content.split('@').map(name => name.trim());

        for (const roomName of roomNames) {
          const rooms = await bot.Room.findAll({ topic: roomName as string });
          const targetRoom = rooms[0];
          if (targetRoom) {
            for (const message of state.textMessages) {
              try {
                await targetRoom.say(message);
              } catch (error) {
                log.error('StarterBot', `Error sending message to ${roomName}:`, error);
                await msg.say(`文字消息发送失败：群聊 ${roomName}`);
              }
            }
            for (const image of state.imageFiles) {
              try {
                await targetRoom.say(image);
              } catch (error) {
                log.error('StarterBot', `Error sending image to ${roomName}:`, error);
                await msg.say(`图片消息发送失败：群聊 ${roomName}`);
              }
            }
            await msg.say(`群聊 ${roomName} 的文字和图片消息已发送！`);
          } else {
            await msg.say(`未找到群聊: ${roomName}`);
          }
        }

        messageState.delete(contact.id);
        await msg.say('所有文字消息和图片已群发完成！');
      }
      return;
    }

    // 自然语言处理：调用百度 API
    if (content.includes('途途')) {
      const context = content.split('途途')[1]?.trim() || '';
      let answer = '哈喽，怎么了小主？途途在的噢';
      if (context) {
        if (!accessToken) {
          await msg.say('途途的 AccessToken 未配置，请通过接口动态设置后再试！');
          return;
        }
        try {
          const res = await axios.post(
            `https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/completions?access_token=${accessToken}`,
            { messages: [ { content: context, role: 'user' } ] },
            { headers: { 'Content-Type': 'application/json' } },
          );
          answer = res.data.result;
        } catch (error) {
          log.error('StarterBot', 'Error calling Baidu API:', error);
          answer = '途途暂时没有办法回答小主的问题，请稍后再试。';
        }
      }
      await msg.say(answer);
    }
  } catch (error) {
    log.error('StarterBot', 'Error handling message:', error);
  }
}

// 配置机器人事件处理
function configureBot (bot: Wechaty, botName: string) {
  bot.on('scan', (qrcode: string, status: ScanStatus) => onScan(qrcode, status, botName));
  bot.on('login', (user: Contact) => onLogin(user, botName));
  bot.on('logout', (user: Contact) => onLogout(user, botName));
  bot.on('message', (msg: Message) => onMessage(msg, bot));
}

// 启动 Web 服务
app.listen(port, () => {
  // eslint-disable-next-line no-console
  console.log(`Wechaty app listening at http://localhost:${port}`);
});

async function executeScheduledTask (task: ScheduledTask) {
  const bot = bots[0];
  if (!bot) {
    log.error('StarterBot', '没有可用的机器人实例来执行定时任务');
    return;
  }

  try {
    const room = await bot.Room.find({ topic: task.target });
    if (room) {
      await room.say(task.content);
      log.info('StarterBot', `定时消息已发送到群聊: ${task.target}`);
    } else {
      log.error('StarterBot', `未找到群聊: ${task.target}`);
    }
  } catch (error) {
    log.error('StarterBot', `发送定时消息到群聊 ${task.target} 时出错:`, error);
  }

  // 移除已完成的任务
  scheduledTasks.splice(scheduledTasks.indexOf(task), 1);
  scheduleNextTask();
}

// 调度下一个最近的定时任务
function scheduleNextTask () {
  if (scheduledTasks.length === 0) {
    return;
  }

  // 找到最近的一个任务
  const now = new Date();
  const nextTask = scheduledTasks.reduce((prev, curr) => prev.time <= curr.time ? prev : curr);

  if (nextTask.time <= now) {
    // 如果任务已经过期，则立即执行
    executeScheduledTask(nextTask)
      .then(() => {
        // 成功处理
        return undefined; // 返回一个值
      })
      .catch(error => {
        log.error('StarterBot', 'Error executing scheduled task:', error);
        return undefined; // 返回一个值
      });
  } else {
    // 否则，设置定时器
    setTimeout(() => {
      executeScheduledTask(nextTask)
        .then(() => {
          // 成功处理
          return undefined; // 返回一个值
        })
        .catch(error => {
          log.error('StarterBot', 'Error executing scheduled task:', error);
          return undefined; // 返回一个值
        });
    }, nextTask.time.getTime() - now.getTime());
  }
}
